package org.ediba.siri.core.image.data;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.ediba.siri.core.Filter;
import org.ediba.siri.core.action.domain.ActionUsed;
import org.ediba.siri.core.common.data.HibernateDomainDao;
import org.ediba.siri.core.image.domain.Image;
import org.ediba.siri.core.image.domain.ImageKeyword;
import org.hibernate.Criteria;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;
import org.hibernate.sql.JoinFragment;

public class HibernateImageDao extends HibernateDomainDao<Image, Integer> implements ImageDao {
	
	public HibernateImageDao() {super(Image.class);}
	
	@Override
	protected DetachedCriteria buildCriteria(Collection<Map<Filter, Object>> filters) {
		DetachedCriteria detachedCriteria = this.buildCriteria();
		
		Disjunction or = Restrictions.disjunction();
		Conjunction and;

		for (Map<Filter, Object> map : filters) {
			and = Restrictions.conjunction();
			for (Map.Entry<Filter,Object> entry: map.entrySet())
				switch (entry.getKey()) {
					case ID: 		and.add(Restrictions.idEq((Integer)entry.getValue()));
					break;
					case ID_MIN: 	and.add(Restrictions.ge(Filter.ID_MIN.getProperty(),(Integer)entry.getValue()));
					break;
					case NAME: 		and.add(Restrictions.ilike(Filter.NAME.getProperty(), (String)entry.getValue(), MatchMode.START));
					break;
					case REQUEST:	if (entry.getValue() == null)
										and.add(Restrictions.isNull(Filter.REQUEST.getProperty()));
									else 
										and.add(Restrictions.eq(Filter.REQUEST.getPropertyId(), entry.getValue()));	
					break;
					case ALBUM:		detachedCriteria.createAlias(Filter.ALBUM.getCollection(),Filter.ALBUM.getProperty());
									and.add(Restrictions.eq(Filter.ALBUM.getPropertyId(), entry.getValue()));
				}
			or.add(and);
		}
		
		detachedCriteria.add(or);
		
		return detachedCriteria;
	}
	
	@Override
	protected DetachedCriteria buildCriteria(Map<Filter, Collection<Object>> filters) {
		DetachedCriteria detachedCriteria = this.buildCriteria();
	
		DetachedCriteria requestC = null;
		DetachedCriteria publicationC = null;
		DetachedCriteria publicationNumberC = null;
		DetachedCriteria imageKeywordC = null;
		DetachedCriteria imageKeywordSubquery = null;
		if (filters.containsKey(Filter.USER)||
				filters.containsKey(Filter.PUBLICATION)||
				filters.containsKey(Filter.REQUEST_DESCRIPTION)||
				filters.containsKey(Filter.REQUEST_TYPE)||
				filters.containsKey(Filter.PUBLICATIONCOUNTRY)||
				filters.containsKey(Filter.PUBLICATIONNUMBER)
				)
			requestC = detachedCriteria.createCriteria(Filter.REQUEST.getProperty(), JoinFragment.LEFT_OUTER_JOIN);
		
		if (filters.containsKey(Filter.PUBLICATIONCOUNTRY)|| filters.containsKey(Filter.PUBLICATION))
			publicationC = requestC.createCriteria(Filter.PUBLICATION.getProperty(), JoinFragment.LEFT_OUTER_JOIN);
		
		if (filters.containsKey(Filter.PUBLICATIONNUMBER))
			publicationNumberC = requestC.createCriteria(Filter.PUBLICATIONNUMBER.getProperty(), JoinFragment.LEFT_OUTER_JOIN);
		
		if (filters.containsKey(Filter.IMAGEKEYWORD)){
			
			imageKeywordC = detachedCriteria.createCriteria(Filter.IMAGEKEYWORD.getCollection(),Filter.IMAGEKEYWORD.getProperty(),JoinFragment.INNER_JOIN);
			
			List<Integer> idsImageKeywordFilter = new ArrayList<Integer>();
			imageKeywordSubquery = DetachedCriteria.forClass(ImageKeyword.class);
			Disjunction ikOr = Restrictions.disjunction();
			
			if (filters.containsKey(Filter.IMAGEKEYWORD))
				for (Object o : filters.get(Filter.IMAGEKEYWORD)) {
					if (o instanceof Integer) idsImageKeywordFilter.add((Integer)o);
					if (o instanceof String) ikOr.add(Restrictions.ilike(Filter.WORD.getProperty(), (String)o,MatchMode.START));
				}
				
			if (idsImageKeywordFilter.size()!=0)
				ikOr.add(Restrictions.in(Filter.ID.getProperty(), idsImageKeywordFilter));
			imageKeywordSubquery.add(ikOr);
			imageKeywordSubquery.setProjection(Projections.property(Filter.ID.getProperty()));
			
			imageKeywordC.add(Subqueries.propertyIn("id",imageKeywordSubquery));

		}		
		
		if (filters.containsKey(Filter.IMAGEKEYWORDA)){
			
			imageKeywordC = detachedCriteria.createCriteria(Filter.IMAGEKEYWORD.getCollection(),Filter.IMAGEKEYWORD.getProperty(),JoinFragment.INNER_JOIN);
			
			List<Integer> idsImageKeywordFilter = new ArrayList<Integer>();
			imageKeywordSubquery = DetachedCriteria.forClass(ImageKeyword.class);
			Disjunction ikOr = Restrictions.disjunction();
			
			if (filters.containsKey(Filter.IMAGEKEYWORDA))
				for (Object o : filters.get(Filter.IMAGEKEYWORDA)) {
					if (o instanceof Integer) idsImageKeywordFilter.add((Integer)o);
					if (o instanceof String) ikOr.add(Restrictions.ilike(Filter.WORD.getProperty(), (String)o,MatchMode.START));
				}

			if (idsImageKeywordFilter.size()!=0)
				ikOr.add(Restrictions.in(Filter.ID.getProperty(), idsImageKeywordFilter));
			imageKeywordSubquery.add(ikOr);
			imageKeywordSubquery.setProjection(Projections.property(Filter.ID.getProperty()));
		
			DetachedCriteria imageKeyword_imageKeywordC = imageKeywordC.createCriteria(Filter.IMAGEKEYWORDA.getCollection(),Filter.IMAGEKEYWORDA.getProperty(),JoinFragment.INNER_JOIN);
			imageKeyword_imageKeywordC.add(Subqueries.propertyIn("id",imageKeywordSubquery));
		}
		
		Disjunction _or;
		Conjunction _and = Restrictions.conjunction();
		
		for (Map.Entry<Filter,Collection<Object>> filter : filters.entrySet()){
			_or = null; 
			if ((filter.getKey() != Filter.IMAGEKEYWORD)&&(filter.getKey() != Filter.IMAGEKEYWORDA))
				_or = Restrictions.disjunction();
			for (Object value : filter.getValue())
				switch (filter.getKey()) {
				case ID: 		_or.add(Restrictions.idEq((Integer)value));
				break;
				case MODEL: 	detachedCriteria.createAlias(Filter.MODEL.getCollection(),Filter.MODEL.getProperty());
								_or.add(Restrictions.eq(Filter.MODEL.getPropertyId(), value));
				break;
				case NAME: 		_or.add(Restrictions.ilike(Filter.NAME.getProperty(), (String)value, MatchMode.START));
				break;
				case REQUEST:	if (value == null)
									_or.add(Restrictions.isNull(Filter.REQUEST.getProperty()));
								else 
									_or.add(Restrictions.eq(Filter.REQUEST.getPropertyId(), value));	
				break;
				case ALBUM:		detachedCriteria.createAlias(Filter.ALBUM.getCollection(),Filter.ALBUM.getProperty());
								_or.add(Restrictions.eq(Filter.ALBUM.getPropertyId(), value));
				break;
				case REQUESTS:	detachedCriteria.createAlias(Filter.REQUESTS.getCollection(),Filter.REQUESTS.getProperty());
								_or.add(Restrictions.eq(Filter.REQUESTS.getPropertyId(), value));
				break;
				case TIMESTAMP: _or.add(Restrictions.eq(Filter.TIMESTAMP.getProperty(), value));
				break;
				case IMAGE_LOCKEDSEARCH: _or.add(Restrictions.eq(Filter.IMAGE_LOCKEDSEARCH.getProperty(), value));
				break;
				case IMAGE_LOCKEDDOWNLOAD: _or.add(Restrictions.eq(Filter.IMAGE_LOCKEDDOWNLOAD.getProperty(), value));
				break;
				case IMAGE_BACKUP: _or.add(Restrictions.eq(Filter.IMAGE_BACKUP.getPropertyId(), value));
				break;
				case REQUEST_TYPE: requestC.add(Restrictions.eq(Filter.REQUEST_TYPE.getProperty(), value));
				break;
				case REQUEST_DESCRIPTION: requestC.add(Restrictions.ilike(Filter.REQUEST_DESCRIPTION.getProperty(), (String)value,MatchMode.ANYWHERE));
				break;
				case IMAGE_IMAGEPROVIDER: _or.add(Restrictions.eq(Filter.IMAGE_IMAGEPROVIDER.getPropertyId(), value));
				break;
				case TIMESTAMP_MIN: _or.add(Restrictions.ge(Filter.TIMESTAMP_MIN.getProperty(), value));
				break;
				case TIMESTAMP_MAX: _or.add(Restrictions.le(Filter.TIMESTAMP_MAX.getProperty(), value));
				break;
				case PUBLICATIONCOUNTRY: publicationC.add(Restrictions.eq(Filter.PUBLICATIONCOUNTRY.getPropertyId(), value));
				break;
				case PUBLICATION:	if (value == null) 
										requestC.add(Restrictions.isNull(Filter.PUBLICATION.getPropertyId()));
									else
										requestC.add(Restrictions.eq(Filter.PUBLICATION.getPropertyId(), value));
				break;
				case PUBLICATIONNUMBER:	if (value == null) 
											publicationNumberC.add(Restrictions.isNull(Filter.PUBLICATIONNUMBER.getPropertyId()));
										else
											publicationNumberC.add(Restrictions.eq(Filter.PUBLICATIONNUMBER.getPropertyId(), value));
				break;
				case USER: requestC.add(Restrictions.eq(Filter.USER.getPropertyId(), value));
				break;
				case IMAGE_CATALOGGED: _or.add(Restrictions.eq(Filter.IMAGE_CATALOGGED.getProperty(), value));
				break;
				case IMAGE_TAGGED: _or.add(Restrictions.eq(Filter.IMAGE_TAGGED.getProperty(), value));
				break;
				case IMAGEKEYWORD:
				break;
				case IMAGEKEYWORDA:
				break;
				}

			if (_or != null)
				_and.add(_or);
		}
			
		detachedCriteria.add(_and);

		return detachedCriteria;
	}

	
	@SuppressWarnings("unchecked")
	@Override
	public List<Image> findByFilters(Map<Filter, Collection<Object>> filters, String sort, boolean dir, Integer start, Integer limit) {
		DetachedCriteria _detachedCriteria;
		if (filters != null) 
			_detachedCriteria = this.buildCriteria(filters); 
		else 
			_detachedCriteria = this.buildCriteria();
		if (sort != null)
			_detachedCriteria.addOrder(this.buildOrder(sort,dir));
		
		_detachedCriteria.setProjection(Projections.distinct(Projections.id()));
		
		List<Integer> l = this.getHibernateTemplate().findByCriteria(_detachedCriteria, start != null ? start : DEFAULT_START, limit != null ? limit : DEFAULT_LIMIT);
		if (!l.isEmpty())
			return this.getHibernateTemplate().findByCriteria(this.buildCriteria().add(Restrictions.in("id", l)));			
		else
			return new ArrayList<Image>(0);
	}	
	
	@SuppressWarnings("unchecked")
	@Override
	public List<Image> findUsedByModel(int idModel) {
		DetachedCriteria actionUsedCriteria = DetachedCriteria.forClass(ActionUsed.class);
		DetachedCriteria imageCriteria = actionUsedCriteria.createCriteria("image", JoinFragment.INNER_JOIN);
		imageCriteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		DetachedCriteria modelCriteria = imageCriteria.createCriteria("models", JoinFragment.INNER_JOIN);
		modelCriteria.add(Restrictions.eq("id", idModel));
		return this.getHibernateTemplate().findByCriteria(imageCriteria);
	}


}
